#include "xmpp_client_connection.h"
#include "xmpp_stream_parser.h"
#include "xmpp_element.h"
#include "features.h"
#include "xmpp_uri.h"
#include <iostream>
#include "bind_iq.h"
#include "session.h"
#include "jid.h"
#include "roster_iq.h"
#include "presence.h"
#include "roster.h"
#include "message.h"



XmppClientConnection::XmppClientConnection():socket_(){

    xmppStreamParser_=new XmppStreamParser();

    bool check = QObject::connect(xmppStreamParser_, SIGNAL(OnStreamStart(XmppElement*)), this, SLOT(OnStreamStart(XmppElement*)));
    Q_ASSERT(check);
    check = QObject::connect(xmppStreamParser_, SIGNAL(OnStreamEnd(XmppElement*)), this, SLOT(OnStreamEnd(XmppElement*)));
    Q_ASSERT(check);
    check = QObject::connect(xmppStreamParser_, SIGNAL(OnStreamElement(XmppElement*)), this, SLOT(OnStreamElement(XmppElement*)));
    Q_ASSERT(check);

    check = QObject::connect(&socket_, SIGNAL(hostFound()), this, SLOT(SocketHostFound()));
    Q_ASSERT(check);
    check = QObject::connect(&socket_, SIGNAL(connected()), this, SLOT(SocketConnected()));
    Q_ASSERT(check);
    check = QObject::connect(&socket_, SIGNAL(disconnected()), this, SLOT(SocketDisconnected()));
    Q_ASSERT(check);
    check = QObject::connect(&socket_, SIGNAL(readyRead()), this, SLOT(SocketReadReady()));
    Q_ASSERT(check);
    check = QObject::connect(&socket_, SIGNAL(encrypted()), this, SLOT(SocketEncrypted()));
    Q_ASSERT(check);
    check = QObject::connect(&socket_, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(SocketSslErrors(const QList<QSslError> &)));
    Q_ASSERT(check);
    check = QObject::connect(&socket_, SIGNAL(error(QAbstractSocket::SocketError)),
        this, SLOT(SocketError(QAbstractSocket::SocketError)));
    Q_ASSERT(check);
}
XmppClientConnection::~XmppClientConnection() {
    delete xmppStreamParser_;
}

void XmppClientConnection::SocketSslErrors(const QList<QSslError> & error) {
    socket_.ignoreSslErrors();
    /*for (int i = 0; i< error.count(); ++i) {
		
        QSslError e=error.at(i);
		QSslError::SslError type=e.error();
                std::cout<<error.at(i).errorString().toStdString();
    }*/
}

void XmppClientConnection::SocketHostFound() {

    emit HostFound();
}

void XmppClientConnection::SocketConnected() {
    SendStartStream();
    emit Connected();
}
    

void XmppClientConnection::SocketDisconnected() {
    //log(QString("Disconnected"));
}

void XmppClientConnection::SocketEncrypted() {
    SendStartStream();
}

void XmppClientConnection::SocketError(QAbstractSocket::SocketError ee) {

}

void XmppClientConnection::SocketReadReady() {
    QByteArray data = socket_.readAll();
    xmppStreamParser_->Parser(data);
}
void XmppClientConnection::SendAuthPlain() {
    QByteArray data = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>";
    QString userPass('\0' + xmppConfiguration_.user() + '\0' + xmppConfiguration_.passwd());
    data += userPass.toUtf8().toBase64();
    data += "</auth>";
    SendToServer(data);
}
void XmppClientConnection::OnStreamElement(XmppElement *xmppElement) {

    QString className(xmppElement->metaObject()->className());
    if (className == "Features" && xmppElement->Xmlns()==XmppUri::STREAM) {
        XmppElement* e = xmppElement->SelectElement("starttls");
        if (e != NULL) {
            if (e->HaveTag("required")) {
                SendStartTls();
                return;
            }
        }
        e = xmppElement->SelectElement("mechanisms");
        if (e != NULL) {
                SendAuthPlain();
        }
        e = xmppElement->SelectElement("bind");
        if (e != NULL) {
             SendBindIQ();
        }
        e = xmppElement->SelectElement("session");
        if (e != NULL) {
             sessionAvaliable_ = true;
        }



   } else
   if (className == "Proceed" && xmppElement->Xmlns()==XmppUri::TLS) {
       socket_.startClientEncryption();
   } else
   if (className == "Success" && xmppElement->Xmlns()==XmppUri::SASL) {
       SendStartStream();
   } else
   if (xmppElement->Xmlns()==XmppUri::CLIENT) {

       if (className == "Presence") {
           ProcessPresence((Presence*)xmppElement);
       } else
       if (className == "Message") {
           ProcessMessage((Message*)xmppElement);
       } else
       if (className == "IQ") {
             IQ *iq = (IQ*)xmppElement;
             QString iqId=iq->Id();
             if (iqId == bind_id_) {
               XmppElement* e = xmppElement->SelectElement("bind",false);
               if ( e != NULL ) {
                   e = e->SelectElement("jid",false);
                   if ( e != NULL ) {

                       xmppConfiguration_.set_resource(e->Value());
                       if (sessionAvaliable_)
                          SendSessionIQ();
                   }
             }
             }else
             if (iqId == session_id_)  {
                 if (iq->type()== "result") {
                     if (autoPresence_)
                         SendAutoPresence();
                     if (autoRoster_)
                         SendAutoRoster();
                 } else
                 if ( iq->type() != "error" ) {

                 }
             } else  {
                 XmppElement* e = xmppElement->SelectElement("query",false);
                 QString classname = e->metaObject()->className();
                 if (classname == "Roster") {
                    //ProcessRosterIq(rosterIq);
                 }

             }


      }

   }
}

void XmppClientConnection::OnStreamEnd(XmppElement *xmppElement) {

}
void XmppClientConnection::OnStreamStart(XmppElement *xmppElement) {

}
void XmppClientConnection::Send(XmppElement *xmppElement) {


}
void XmppClientConnection::SendStartStream() {
    QByteArray data = "<?xml version='1.0'?><stream:stream to='";
    data.append(xmppConfiguration_.domain());
    data.append("' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>");
    SendToServer(data);
}
void XmppClientConnection::SendToServer(const QByteArray& data) {
    socket_.write(data);
}
void XmppClientConnection::SendStartTls()
{
    SendToServer("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
}
void XmppClientConnection::ConnectToServer(const QString& host, const QString& user, const QString& passwd,
                     const QString& domain, int port) {

    xmppConfiguration_.set_host(host);
    xmppConfiguration_.set_user(user);
    xmppConfiguration_.set_passwd(passwd);
    xmppConfiguration_.set_domain(domain);
    xmppConfiguration_.set_port(port);
    socket_.connectToHost(host,port);
}
void XmppClientConnection::SendBindIQ() {
    BindIq bindIq;
    xmppConfiguration_.set_resource("zfpear");
    bindIq.GetBind()->SetResource(xmppConfiguration_.resource());
    bindIq.SetType("set");
    bindIq.GenerateId();
    bind_id_=bindIq.Id();
    QByteArray iqData=bindIq.ToString().toAscii();
    SendToServer(iqData);
}

void XmppClientConnection::SendSessionIQ() {
    Session *session=new Session();
    IQ iq;
    iq.SetType("set");
    Jid jid;
    jid.FromString(xmppConfiguration_.domain());
    iq.SetTo(jid);
    iq.GenerateId();
    session_id_ = iq.Id();
    iq.AppendChild(session);
    QByteArray iqData=iq.ToString().toAscii();
    SendToServer(iqData);
 }
void XmppClientConnection::SendAutoRoster() {
    RosterIq rosterIq;
    rosterIq.SetType("get");
    rosterIq.GenerateId();
    rosterIq.SetFrom(xmppConfiguration_.GetJid());
    SendToServer(rosterIq.ToByteArray());
}
void XmppClientConnection::SendAutoPresence() {
    Presence presence;
    presence.SetShow(xmppConfiguration_.GetShowType());
    QList<QString> statusList;
    statusList.append(xmppConfiguration_.GetPresenceStatus());
    presence.SetStatus(statusList);
    SendToServer(presence.ToByteArray());
}
void XmppClientConnection::ProcessPresence(Presence* presence) {

}

void XmppClientConnection::ProcessMessage(Message* message) {

}
void XmppClientConnection::ProcessRosterIq(Roster* roster) {

}
